//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Data.Common;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Web;
using SR = Microsoft.Research.DataLayer;
using System.Runtime.Remoting.Contexts;

namespace DataLayerBroker
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    //[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    [ServiceBehavior]
    public class DLBrokerService : IDataLayerBroker
    {
        private SR.Connection conn;
        private Storage objType;
        private const string PROTOCOL_SESSION_NAME = "Protocol";
        
        static DLBrokerService()
        {
            SR.SR_Connection.Init();
        }

        public DLBrokerService()
        {
            //SR.ConnectionManager.AlternateProviderLocation = @"E:\src\msr\sd\trident\4\Trident\Research\DataLayer\Services\DataLayerBroker\bin";
            SR.ConnectionManager.AlternateProviderLocation = System.Web.HttpRuntime.BinDirectory;

            SR.ConnectionManager mgr = SR.ConnectionManager.CreateForAgent(
                SR.ConnectionManager.CancelBehavior.ThrowException,
                SR.ConnectionManager.Agent.WebService);
            conn = mgr.PickConnection(SR.ConnectionManagerBase.ConnectionUI.NeverShowUI);
        }


        /// <summary>
        /// Gets or sets the serialization protocol.
        /// </summary>
        /// <value>The serialization protocol.</value>
        private SerializationProtocol SerializationProtocol
        {
            get
            {
                if (HttpContext.Current.Session[PROTOCOL_SESSION_NAME] != null)
                {
                    return (SerializationProtocol)HttpContext.Current.Session[PROTOCOL_SESSION_NAME];
                }
                else
                {
                    return SerializationProtocol.SOAP;
                }
            }
            set
            {
                HttpContext.Current.Session[PROTOCOL_SESSION_NAME] = value;
            }
        }

        public void Connect()
        {
        }

        /// <summary>
        /// Sets the serialization protocol.
        /// </summary>
        /// <param name="protocol">The protocol.</param>
        public void SetSerializationProtocol(SerializationProtocol protocol)
        {
            SerializationProtocol = protocol;
        }

        public void Object(Storage obj, SR.Connection.Action action, ref List<Parameter> paramList)
        {
            List<SR.Parameter> paramList2 = new List<SR.Parameter>();

            objType = obj;

            // Load parameters into list
            foreach (Parameter p in paramList)
            {
                p.protocol = this.SerializationProtocol;
                SR.Parameter inParam = p.FromNetwork();
                paramList2.Add(inParam);
            }
            obj.protocol = this.SerializationProtocol;
            SR.Connection.Storage objSR = obj.FromNetwork();

            // Call registry service
            conn.Object(objSR, action, ref paramList2);

            // Marshal results back to client
            List<Parameter> results = new List<Parameter>(paramList);
            paramList.Clear();
            foreach (SR.Parameter p in paramList2)
            {
                paramList.Add(Parameter.ToNetwork(p, this.SerializationProtocol));
            }
        }

        public void Object(Storage obj, SR.Connection.Action action, ref List<Parameter> paramList,
            out List<XferObjectInstance> results)
        {
            List<SR.Parameter> paramList2 = new List<SR.Parameter>();

            objType = obj;

            // Load parameters into list
            if (action == Microsoft.Research.DataLayer.Connection.Action.Search)
            {
                paramList[0].protocol = this.SerializationProtocol;
                paramList2.Add(paramList[0].FromNetworkSearch(conn));
            }
            else
            {
                foreach (Parameter p in paramList)
                {
                    p.protocol = this.SerializationProtocol;
                    paramList2.Add(p.FromNetwork());
                }
            }

            List<SR.IObject> results2 = new List<SR.IObject>();
            obj.protocol = this.SerializationProtocol;
            conn.Object(obj.FromNetwork(), action, ref paramList2, CreateTransferObject, out results2);


            results = new List<XferObjectInstance>();
            results.AddRange(Array.ConvertAll<SR.IObject, XferObjectInstance>(results2.ToArray(),
                new Converter<Microsoft.Research.DataLayer.IObject, XferObjectInstance>(delegate(SR.IObject src)
                {
                    return (XferObjectInstance)src;
                }
            )));
        }

        public void Relation(SR.Connection.Action action, XferObjectReference obj1ref, XferObjectReference obj2ref)
        {
            obj1ref.protocol = this.SerializationProtocol;
            obj2ref.protocol = this.SerializationProtocol;
            SR.IObject obj1 = obj1ref.Create(conn);
            SR.IObject obj2 = obj2ref.Create(conn);
            conn.Relation(action, obj1, obj2);
        }

        public void Relation(Microsoft.Research.DataLayer.Connection.Action action,
            XferObjectReference obj1Ref, XferExtraRelDataReference objRelRef)
        {
            obj1Ref.protocol = this.SerializationProtocol;
            SR.IObject obj1 = obj1Ref.Create(conn);

            objRelRef.protocol = this.SerializationProtocol;
            SR.IObject obj2 = objRelRef.Create(conn);

            conn.Relation(action, obj1, obj2);
        }

        public void Relation(SR.Connection.Action action, XferObjectReference obj1, string field, Storage type2, out List<XferObjectInstance> results)
        {
            obj1.protocol = this.SerializationProtocol;
            type2.protocol = this.SerializationProtocol;
            SR.IObject refObj = obj1.Create(conn);
            List<SR.IObject> results2 = new List<SR.IObject>();
            conn.Relation(action, refObj, field, type2.FromNetwork(), CreateTransferRelation, out results2);

            results = new List<XferObjectInstance>();
            results.AddRange(Array.ConvertAll<SR.IObject, XferObjectInstance>(results2.ToArray(),
                new Converter<Microsoft.Research.DataLayer.IObject, XferObjectInstance>(delegate(SR.IObject src)
                {
                    // CreateTransferObject creates XferObjectInstance objects. Safe to cast.
                    return (XferObjectInstance)src;
                }
            )));
        }

        public void Relation(SR.Connection.Action action, XferObjectReference obj1, Storage obj2, string revKey, ref List<Parameter> paramList)
        {
            obj1.protocol = this.SerializationProtocol;
            obj2.protocol = this.SerializationProtocol;
            SR.IObject refObj = obj1.Create(conn);
            List<SR.Parameter> paramList2 = new List<SR.Parameter>();
            foreach (Parameter p in paramList)
            {
                p.protocol = this.SerializationProtocol;
                paramList2.Add(p.FromNetwork());
            }
            conn.Relation(action, refObj, obj2.FromNetwork(), revKey, ref paramList2);
        }

        private SR.IObject CreateTransferRelation(DbDataReader input, SR.Connection c)
        {
            XferObjectInstance ret = new XferObjectInstance();
            ret.ID = Guid.Empty;
            ret.Type = objType;
            ret.Fields = new List<Parameter>();

            for (int i = 0; i < input.FieldCount; i++)
            {
                string name = input.GetName(i);
                Type fieldType = input.GetFieldType(i);
                SR.Parameter.ParamType paramType;

                paramType = SR.Connection.ClrTridentTypeMap[fieldType];
                object paramValue = input.GetValue(i);

                SR.Parameter param = new SR.Parameter(name, paramType, SR.Parameter.ParamDirection.Unknown, paramValue);
                Parameter xferParam = Parameter.ToNetwork(param, this.SerializationProtocol);

                ret.Fields.Add(xferParam);
            }
            return ret;
        }

        private SR.IObject CreateTransferObject(DbDataReader input, SR.Connection c)
        {
            XferObjectInstance ret = new XferObjectInstance();
            ret.ID = input.GetGuid(input.GetOrdinal("ID"));
            ret.Type = objType;
            ret.Fields = new List<Parameter>();

            for (int i = 0; i < input.FieldCount; i++)
            {
                string name = input.GetName(i);
                Type fieldType = input.GetFieldType(i);
                SR.Parameter.ParamType paramType;

                paramType = SR.Connection.ClrTridentTypeMap[fieldType];

                object paramValue = input.GetValue(i);

                SR.Parameter param = new SR.Parameter(name, paramType, SR.Parameter.ParamDirection.Unknown, paramValue);
                Parameter xferParam = Parameter.ToNetwork(param, this.SerializationProtocol);

                ret.Fields.Add(xferParam);
            }

            return ret;
        }

        private delegate void ConnAtomCallback(SR.IConnectionAtomicity atom);
        private void ExecConnectionAtomicity(ConnAtomCallback callback)
        {
            if (conn is SR.IConnectionAtomicity)
            {
                callback((SR.IConnectionAtomicity)conn);
            }
        }

        public void ConnAtom_BeginEdit()
        {
            ExecConnectionAtomicity(new ConnAtomCallback(delegate(SR.IConnectionAtomicity atom)
            {
                atom.BeginEdit();
            }));
        }

        public void ConnAtom_EndEdit()
        {
            ExecConnectionAtomicity(new ConnAtomCallback(delegate(SR.IConnectionAtomicity atom)
            {
                atom.EndEdit();
            }));
        }

        public void ConnAtom_CancelEdit()
        {
            ExecConnectionAtomicity(new ConnAtomCallback(delegate(SR.IConnectionAtomicity atom)
            {
                atom.CancelEdit();
            }));
        }


    }
}
